#include "ThreadPool.h"
#include "Mutex.h"
#include "Event.h"
#include <iostream>

#ifdef _WIN32
#ifndef AF_IPX
#include <windows.h>
#endif
#else
#include <pthread.h>
#include <sys/types.h>
#endif

ThreadPool* ThreadPool::_instance = NULL;

ThreadPool *ThreadPool::GetThreadPool()
{
	return ThreadPool::_instance;
}

void ThreadPool::InitThreadPool(int nbThread)
{
	ThreadPool::_instance = new ThreadPool(nbThread);
}

ThreadPool::ThreadPool(int nbThread) :
_maxThread(nbThread),
_curThread(0),
_freeThread(0)
{
	_waitingRequest = new Mutex();
	_accessingRequest = new Mutex();
	_infoThread = new Mutex();
	_requestAvailable = new Event(false);
}

void   ThreadPool::checkThread()
{
	int currRequest =	this->_moderatePriorityRequest.size() +
		this->_highPriorityRequest.size() + this->_lowPriorityRequest.size();
	if (this->_freeThread >= currRequest || this->_curThread >= this->_maxThread)
		return;
	this->_infoThread->Lock();
	this->_freeThread++;
	this->_curThread++;
	this->_infoThread->Unlock();
	
	#ifdef _WIN32
	HANDLE h = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ThreadProc, this, 0, NULL);
	#else
	pthread_t _handle;
	pthread_create(&_handle, NULL, ThreadProc, this);
	#endif
}

void *ThreadProc (void *lpParameter)
{
	ThreadPool *param = (ThreadPool*) lpParameter;
	ThreadPool::Request *request = NULL;

	while (true)
	{
		if (request)
		{
			(*request->function)(request->parameters);
			if (request->autoFree)
				delete request;
			param->_infoThread->Lock();
			param->_freeThread++;
			param->_infoThread->Unlock();
			
		}
		param->_waitingRequest->Lock();
		param->_requestAvailable->Wait();
		param->_infoThread->Lock();
		param->_freeThread--;
		param->_infoThread->Unlock();
		param->_accessingRequest->Lock();
	
		if (!param->_highPriorityRequest.empty())
		{
			request = param->_highPriorityRequest.front();
			param->_highPriorityRequest.pop_front();
		}
		else if (!param->_moderatePriorityRequest.empty())
		{
			request = param->_moderatePriorityRequest.front();
			param->_moderatePriorityRequest.pop_front();
		}
		else if (!param->_lowPriorityRequest.empty())
		{
			request = param->_lowPriorityRequest.front();
			param->_lowPriorityRequest.pop_front();
		}
		if (param->_moderatePriorityRequest.empty() &&
			param->_lowPriorityRequest.empty() &&
			param->_highPriorityRequest.empty())
			param->_requestAvailable->Reset();
		param->_accessingRequest->Unlock();
		param->_waitingRequest->Unlock();
	}
	return 0;
}

void ThreadPool::AddRequest(ThreadPool::Request *request, ThreadPool::Priority priority)
{
	this->_accessingRequest->Lock();	
	if (priority == HIGH)
		this->_highPriorityRequest.push_back(request);
	else if (priority == MODERATE)
		this->_moderatePriorityRequest.push_back(request);
	else
		this->_lowPriorityRequest.push_back(request);
	this->checkThread();
	this->_requestAvailable->Set();
	this->_accessingRequest->Unlock();
}
